<?php

  #doc
  #	classname:	MiiMigrations
  #	scope:		PUBLIC
  #
  #/doc
  
  class MiiMigrations
  {
    const DOWN_TO_VERSION   =   -1;
    const UP_TO_VERSION =   1;
    
    
    const BASE_MIGRATION='m000000_000000_base';
  
    /**
     * @var string the directory that stores the migrations. This must be specified
     * in terms of a path alias, and the corresponding directory must exist.
     * Defaults to 'application.migrations' (meaning 'protected/migrations').
     */
    public $migrationPath='application.migrations';
    /**
     * @var string the name of the table for keeping applied migration information.
     * This table will be automatically created if not exists. Defaults to 'tbl_migration'.
     * The table structure is: (version varchar(255) primary key, apply_time integer)
     */
    public $migrationTable='tbl_migration';
    /**
     * @var string the application component ID that specifies the database connection for
     * storing migration information. Defaults to 'db'.
     */
    public $connectionID='db';
    /**
     * @var string the path of the template file for generating new migrations. This
     * must be specified in terms of a path alias (e.g. application.migrations.template).
     * If not set, an internal template will be used.
     */
    public $templateFile;
    /**
     * @var string the default command action. It defaults to 'up'.
     */
    public $defaultAction='up';
    
    public function __construct()
    {
      $this->migrationPath=YiiBase::getPathOfAlias($this->migrationPath);
    }
    /**
    *
    *
    *
    */
    private $_db;
    protected function getDbConnection()
    {
      if($this->_db!==null)
        return $this->_db;
      else if(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection)
      {
        $this->_db->setActive(true);
        return $this->_db;
      }
      else; //TODO: Error reporting fo database connection
    }
    
    /**
    *
    *
    *
    */
    protected function getMigrationHistory($limit)
    {
      $db=$this->getDbConnection();
      if($db->schema->getTable($this->migrationTable)===null)
      {
        $db->createCommand()->createTable($this->migrationTable, array(
          'version'=>'string NOT NULL PRIMARY KEY',
          'apply_time'=>'integer',
        ));
        $db->createCommand()->insert($this->migrationTable, array(
          'version'=>self::BASE_MIGRATION,
          'apply_time'=>time(),
        ));
      }
      return CHtml::listData($db->createCommand()
        ->select('version, apply_time')
        ->from($this->migrationTable)
        ->order('version DESC')
        ->limit($limit)
        ->queryAll(),'version', 'apply_time');
    }
    
    /**
    *
    *
    *
    */
    public function getAllMigrations ()
    {
      $migrations=array();
      foreach($this->getMigrationHistory(-1) as $version=>$time){
        $migrations[substr($version,1,13)]->applied=true;
        $migrations[substr($version,1,13)]->apply_time=$time;
      }
  
      $handle=opendir($this->migrationPath);
      while(($file=readdir($handle))!==false)
      {
        if($file==='.' || $file==='..')
          continue;
        $path=$this->migrationPath.DIRECTORY_SEPARATOR.$file;
        if(preg_match('/^(m(\d{6}_\d{6})_(.*?))\.php$/',$file,$matches) && is_file($path)){
          $migrations[$matches[2]]->migration=$matches[1];
          $migrations[$matches[2]]->name=$matches[3];
          if(!isset($migrations[$matches[2]]->applied)) $migrations[$matches[2]]->applied=false;
        }
      }
      closedir($handle);
      return $migrations;
    }
    
    public function getCurrentVersion ()
    {
        $keys=array_keys($this->getMigrationHistory(1));
        return substr($keys[0],1,13);
    }
    
    protected function getMigrationsListTo ($version)
    {
        $migrations=$this->getAllMigrations();
        $currentMigration=$this->getCurrentVersion();
        if ($version==$currentMigration) return false;
        if (!isset($migrations[$version])) return false;
        $action=0;
        $migrationsList=array();
        $do=false;
        foreach( $migrations as $id => $migration )
        {
            if (($action==0)&&($id==$currentMigration)) $action=self::UP_TO_VERSION;
            if (($action==0)&&($id==$version)) $action=self::DOWN_TO_VERSION;
            switch ( $action )
            {
                case self::UP_TO_VERSION:
                    if ( $do )
                    {
                      if (!$migration->applied){
                        $mgr_l = false;
                        $mgr_l->action=self::UP_TO_VERSION;
                        $mgr_l->migration=$migration->migration;
                        $migrationsList[]=$mgr_l;
                      }
                    }
                    if ($id==$version) $do=false;
                    if ($id==$currentMigration) $do=true;
                break;
                
                case self::DOWN_TO_VERSION:
                    if ( $do )
                    {
                      if ($migration->applied){
                        $mgr_l = false;
                        $mgr_l->action=self::DOWN_TO_VERSION;
                        $mgr_l->migration=$migration->migration;
                        $migrationsList[]=$mgr_l;
                      }
                    }
                    if ($id==$version) $do=true;
                    if ($id==$currentMigration) $do=false;
                break;
            }
        }
        return ($action==self::UP_TO_VERSION) ? $migrationsList : array_reverse($migrationsList);
    }
  
  protected function instantiateMigration($class)
  {
    $file=$this->migrationPath.DIRECTORY_SEPARATOR.$class.'.php';
    require_once($file);
    return new $class;
  }
  
  protected function migrate($class,$action)
  {
    if ($action==0) return false;
    if($class===self::BASE_MIGRATION)
    {
      return (object) array(
          'message' => 'You trying to migrate to BASE_MIGRATION!',
          'type'  =>  'error',
          'code'  =>  '001',
      );
    }
    
    $start=microtime(true);
    $migration=$this->instantiateMigration($class);
    switch ( $action )
    {
      case self::DOWN_TO_VERSION:
        $migration->down();
      break;
      
      case self::UP_TO_VERSION:
        $migration->up();
      break;
    }
    $this->getDbConnection()->createCommand()->insert($this->migrationTable, array(
      'version'=>$class,
      'apply_time'=>time(),
    ));
    $time=microtime(true)-$start;
    return (object) array(
      'time'  =>  $time,
      'code'  =>  '101',
      'message' =>  '',
      'message' =>  $action == self::UP_TO_VERSION ? "*** applied".$class." (time: ".sprintf("%.3f",$time)."s)\n\n" : "*** reverted ".$class." (time: ".sprintf("%.3f",$time)."s)\n\n",
      'result'  =>  'success',
    );
  }
  
  public function MigrateTo ($version)
  {
    $migrationsList=$this->getMigrationsListTo($version);
    if(!$migrations) return false;
    foreach( $migrationsList as $id => $migration )
    {
      $this->migrate($migration->migration,$migration->action);
    }
  }
  
  public function getlist ( $version)
  {
    return $this->getMigrationsListTo($version);
  }
  
  
  }
?>